// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <gtest/gtest.h>
#include <climits>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cantor/parser/parser.h>
#include <cantor/utility/json_reader.h>
#include <cantor/transform/transform.h>
#include <tests/config.h>
#include <tests/paser/node_check.h>

int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

namespace cantor {

    class DataJson {
    public:
        static DataJson &instance() {
            static DataJson instance;
            return instance;
        }

        const nlohmann::ordered_json &json() const { return json_; }

    private:
        DataJson() {
            std::string json_path = turbo::str_cat(testing::test_root, "/data/create_table.json");
            auto rs = read_file(json_path);
            KCHECK(rs.ok());
            json_ = std::move(rs).value_or_die();
        }

        nlohmann::ordered_json json_;
    };

    TEST(create_table, table_0) {
        cantor::parser::SqlParser parser;

        std::string sql = "create table score_diary_book ("
                "`book_id` bigint(20) NOT NULL COMMENT '日记本ID',"
                "`parent_id` bigint(20) NOT NULL COMMENT '父ID',"
                "`score_type` int(11) NOT NULL COMMENT '1-日记本净分数 2-日记本总分',"
                "`score` double NOT NULL COMMENT '分数',"
                "`level` int(11) NOT NULL COMMENT '级别 0-不合格 1-普通 2-优秀 3-超优秀',"
                "`state` int(11) NOT NULL COMMENT '状态 1-审核通过 2-审核拒绝 0-审核中 ',"
                "`i8` int6 NOT NULL,"
                "`update_time` datetime NOT NULL,"
                "`create_time` datetime NOT NULL,"
                "PRIMARY KEY (book_id,score_type),"
                "KEY score_type (score_type),"
                "KEY level (level),"
                "KEY state (state)"
                ") ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=500 COMMENT='{\"comment\":"
                ", \"resource_tag\":\"e0-nj\", \"namespace\":\"Kumo\"}'";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        parser.parse(sql);
        ASSERT_EQ(cantor::parser::SUCCESS, parser.error)<<parser.syntax_err_str;
        ASSERT_EQ(1, parser.result.size());
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, parser.result[0]->stmt_type);
        auto *stmt = (cantor::parser::CreateTableStmt *) parser.result[0];
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, stmt->stmt_type);
        ASSERT_FALSE(stmt->if_not_exist);

        auto j = stmt->to_json();
        //std::cout<<j<<std::endl;

        auto &json = DataJson::instance().json();
        auto &r = json["table_0"];
        if (!r.empty()) {
            ASSERT_EQ(j, r);
            KLOG(INFO)<<NOPREFIX<<"compare stand json ast ...";
        }
        KLOG(INFO)<<NOPREFIX<<"testing done ..............";

    }
    TEST(test_parser, case_create_table_1) {
        cantor::parser::SqlParser parser;

        std::string sql = "create table score_diary_book ("
                "`book_id` bigint(20) NOT NULL COMMENT '日记本ID',"
                "`parent_id` bigint(20) NOT NULL COMMENT '父ID',"
                "`score_type` int(11) NOT NULL COMMENT '1-日记本净分数 2-日记本总分',"
                "`score` double NOT NULL COMMENT '分数',"
                "`level` int(11) NOT NULL COMMENT '级别 0-不合格 1-普通 2-优秀 3-超优秀',"
                "`state` int(11) NOT NULL COMMENT '状态 1-审核通过 2-审核拒绝 0-审核中 ',"
                "`update_time` datetime NOT NULL,"
                "`create_time` datetime[] NOT NULL,"
                "`mytype` mytype[] NOT NULL,"
                "`i8` int8 NOT NULL,"
                "`map` map(int, int) NOT NULL,"
                "PRIMARY KEY (book_id,score_type),"
                "KEY score_type (score_type),"
                "KEY level (level),"
                "KEY state (state)"
                ") ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=500 COMMENT='{\"comment\":\"\""
                ", \"resource_tag\":\"e0-nj\", \"namespace\":\"Kumo\"}'";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        parser.parse(sql);
        ASSERT_EQ(cantor::parser::SUCCESS, parser.error)<<parser.syntax_err_str;
        ASSERT_EQ(1, parser.result.size());
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, parser.result[0]->stmt_type);
        cantor::parser::CreateTableStmt *stmt = (cantor::parser::CreateTableStmt *) parser.result[0];
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, stmt->stmt_type);
        ASSERT_FALSE(stmt->if_not_exist);

        auto j = stmt->to_json();
        //std::cout<<j<<std::endl;
        auto &json = DataJson::instance().json();
        auto &r = json["table_1"];
        if (!r.empty()) {
            ASSERT_EQ(j, r);
            KLOG(INFO)<<NOPREFIX<<"compare stand json ast ...";
        }
        KLOG(INFO)<<NOPREFIX<<"testing done ..............";
    }

    TEST(test_parser, case_create_table_hll) {
        cantor::parser::SqlParser parser;

        std::string sql = "create table score_diary_book ("
                "`book_id` bigint(20) NOT NULL COMMENT '日记本ID',"
                "`parent_id` bigint(20) NOT NULL COMMENT '父ID',"
                "`score_type` int(11) NOT NULL COMMENT '1-日记本净分数 2-日记本总分',"
                "`score` double NOT NULL COMMENT '分数',"
                "`level` int(11) NOT NULL COMMENT '级别 0-不合格 1-普通 2-优秀 3-超优秀',"
                "`state` int(11) NOT NULL COMMENT '状态 1-审核通过 2-审核拒绝 0-审核中 ',"
                "`update_time` datetime NOT NULL,"
                "`create_time` datetime NOT NULL,"
                "`hll_field` HLL NOT NULL,"
                "PRIMARY KEY (book_id,score_type),"
                "KEY score_type (score_type),"
                "KEY level (level),"
                "KEY state (state)"
                ") ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=500 COMMENT='{\"comment\":"
                ", \"resource_tag\":\"e0-nj\", \"namespace\":\"Kumo\"}'";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        parser.parse(sql);
        ASSERT_EQ(cantor::parser::SUCCESS, parser.error);
        ASSERT_EQ(1, parser.result.size());

        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, parser.result[0]->stmt_type);
        cantor::parser::CreateTableStmt *stmt = (cantor::parser::CreateTableStmt *) parser.result[0];
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, stmt->stmt_type);
        ASSERT_FALSE(stmt->if_not_exist);


        auto j = stmt->to_json();
        //std::cout<<j<<std::endl;
        auto &json = DataJson::instance().json();
        auto &r = json["case_create_table_hll"];
        if (!r.empty()) {
            ASSERT_EQ(j, r);
            KLOG(INFO)<<NOPREFIX<<"compare stand json ast ...";
        }
        KLOG(INFO)<<NOPREFIX<<"testing done ..............";
    }

    TEST(test_parser, case_create_rollup_table) {
        cantor::parser::SqlParser parser;

        std::string sql = "create table score_diary_book ("
                "`book_id` bigint(20) NOT NULL COMMENT '日记本ID',"
                "`parent_id` bigint(20) NOT NULL COMMENT '父ID',"
                "`score_type` int(11) NOT NULL COMMENT '1-日记本净分数 2-日记本总分',"
                "`score` double NOT NULL COMMENT '分数',"
                "`__sign__` bigint(20) unsigned NOT NULL DEFAULT '0',"
                "`__version__` bigint(20) unsigned NOT NULL DEFAULT '0',"
                "PRIMARY KEY (book_id, score_type, __sign__),"
                "ROLLUP KEY `rollup_index` (book_id)"
                ") ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=500 COMMENT='{\"comment\":"
                ", \"resource_tag\":\"e0-nj\", \"namespace\":\"Kumo\"}'";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        parser.parse(sql);
        ASSERT_EQ(cantor::parser::SUCCESS, parser.error)<<parser.syntax_err_str;
        ASSERT_EQ(1, parser.result.size());
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, parser.result[0]->stmt_type);
        cantor::parser::CreateTableStmt *stmt = (cantor::parser::CreateTableStmt *) parser.result[0];
        ASSERT_EQ(cantor::pb::ast::STMT_CREATE_TABLE, stmt->stmt_type);
        ASSERT_FALSE(stmt->if_not_exist);

        auto j = stmt->to_json();
        //std::cout<<j<<std::endl;
        auto &json = DataJson::instance().json();
        auto &r = json["case_create_rollup_table"];
        if (!r.empty()) {
            ASSERT_EQ(j, r);
            KLOG(INFO)<<NOPREFIX<<"compare stand json ast ...";
        }
        KLOG(INFO)<<NOPREFIX<<"testing done ..............";
    }
    TEST(test_parser, case_create_check_table) {
        cantor::parser::SqlParser parser;

        std::string sql = "create table score_diary_book ("
                "`book_id` bigint(20) NOT NULL COMMENT '日记本ID',"
                "`parent_id` bigint(20) NOT NULL CHECK (parent_id < 10 AND parent_id > 1)   COMMENT '父ID',"
                "`score_type` int(11) NOT NULL COMMENT '1-日记本净分数 2-日记本总分',"
                "`score` double NOT NULL COMMENT '分数',"
                "`__sign__` bigint(20) unsigned NOT NULL DEFAULT '0',"
                "`__version__` bigint(20) unsigned NOT NULL DEFAULT '0',"
                "PRIMARY KEY (book_id, score_type, __sign__),"
                "CHECK (book_id > 0 AND book_id > parent_id)"
                ") ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=500 COMMENT='{\"comment\":"
                ", \"resource_tag\":\"e0-nj\", \"namespace\":\"Kumo\"}'";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        parser.parse(sql);
        transform::Transform tans;
        ASSERT_TRUE(tans.resolve_ast(&parser).ok());
        KLOG(INFO)<<NOPREFIX<<tans.stmts.DebugString();
        KLOG(INFO)<<NOPREFIX<<tans.stmts.statements(0).DebugString();
        auto s =tans.stmts.SerializeAsString();
        pb::ast::Statements r;
        ASSERT_TRUE(r.ParseFromString(s));
    }
}  // namespace cantor
